package com.zenith.storage.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zenith.common.result.PageResult;
import com.zenith.storage.mapper.StorageFileMapper;
import com.zenith.storage.po.domain.StorageFile;
import com.zenith.storage.po.dto.FileQueryDTO;
import com.zenith.storage.po.dto.UploadInitDTO;
import com.zenith.storage.service.StorageFileService;
import com.zenith.storage.util.FileUtils;
import com.zenith.storage.po.vo.FileVO;
import com.zenith.storage.po.vo.UploadInitVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 文件管理服务实现类
 * @author 22071
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class StorageFileServiceImpl extends ServiceImpl<StorageFileMapper, StorageFile> implements StorageFileService {
    
    private final StorageFileMapper storageFileMapper;
    
    // 本地存储路径配置
    @Value("${storage.local.path:/uploads}")
    private String localStoragePath;
    
    @Override
    public UploadInitVO initUpload(UploadInitDTO uploadInitDTO) {
        UploadInitVO uploadInitVO = new UploadInitVO();
        
        // 检查是否已存在相同MD5的文件（秒传）
        if (StringUtils.hasText(uploadInitDTO.getMd5())) {
            StorageFile existingFile = storageFileMapper.selectOne(
                    new LambdaQueryWrapper<StorageFile>()
                            .eq(StorageFile::getMd5, uploadInitDTO.getMd5())
                            .eq(StorageFile::getStatus, 1));
            
            if (existingFile != null) {
                // 秒传逻辑
                uploadInitVO.setFileId(existingFile.getId());
                // 这里应该返回已存在的文件信息，而不是重新上传
            }
        }
        
        // 判断是否需要分片上传
        if (uploadInitDTO.getChunkSize() != null && uploadInitDTO.getChunkSize() > 0) {
            uploadInitVO.setIsChunked(true);
            uploadInitVO.setChunkSize(uploadInitDTO.getChunkSize());
            int chunkTotal = (int) Math.ceil((double) uploadInitDTO.getFileSize() / uploadInitDTO.getChunkSize());
            uploadInitVO.setChunkTotal(chunkTotal);
        } else {
            uploadInitVO.setIsChunked(false);
        }
        
        // 创建文件记录
        StorageFile storageFile = new StorageFile();
        storageFile.setOriginalName(uploadInitDTO.getFileName());
        storageFile.setFileSize(uploadInitDTO.getFileSize());
        storageFile.setMd5(uploadInitDTO.getMd5());
        storageFile.setStatus(1);
        storageFile.setUploadStatus(0);
        
        storageFileMapper.insert(storageFile);
        uploadInitVO.setFileId(storageFile.getId());
        
        return uploadInitVO;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public FileVO uploadFile(MultipartFile file, UploadInitVO uploadInitVO) {
        // 获取文件记录
        StorageFile storageFile = storageFileMapper.selectById(uploadInitVO.getFileId());
        if (storageFile == null) {
            throw new RuntimeException("文件记录不存在");
        }
        
        // 更新文件信息
        storageFile.setMimeType(file.getContentType());
        storageFile.setExtension(FileUtils.getFileExtension(file.getOriginalFilename()));
        storageFile.setUploadStatus(2); // 上传完成
        
        storageFileMapper.updateById(storageFile);
        
        // 转换为VO对象
        return convertToVO(storageFile);
    }
    
    @Override
    public void downloadFile(Long fileId, HttpServletResponse response) {
        StorageFile storageFile = storageFileMapper.selectById(fileId);
        if (storageFile == null) {
            throw new RuntimeException("文件不存在");
        }

        // 设置响应头
        response.setContentType(storageFile.getMimeType());
        response.setHeader("Content-Disposition", "attachment; filename=" +
                URLEncoder.encode(storageFile.getOriginalName(), StandardCharsets.UTF_8));
        response.setContentLengthLong(storageFile.getFileSize());

        try {
            // 构建文件完整路径
            String fileKey = storageFile.getFileKey();
            if (fileKey == null || fileKey.isEmpty()) {
                // 如果fileKey为空，使用默认路径构造
                fileKey = storageFile.getId() + "_" + storageFile.getOriginalName();
            }
            
            // 构造完整文件路径
            String fullPath = Paths.get(localStoragePath, fileKey).toString();
            
            // 从本地文件系统下载文件
            FileUtils.downloadFile(fullPath, response.getOutputStream());
        } catch (IOException e) {
            log.error("文件下载失败: {}", e.getMessage(), e);
            throw new RuntimeException("文件下载失败", e);
        }
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteFile(Long fileId) {
        StorageFile storageFile = new StorageFile();
        storageFile.setId(fileId);
        storageFile.setStatus(0); // 软删除
        return storageFileMapper.updateById(storageFile) > 0;
    }
    
    @Override
    public FileVO getFileDetail(Long fileId) {
        StorageFile storageFile = storageFileMapper.selectById(fileId);
        if (storageFile == null) {
            return null;
        }
        return convertToVO(storageFile);
    }
    
    @Override
    public PageResult<FileVO> listFiles(FileQueryDTO queryDTO) {
        // 构建查询条件
        LambdaQueryWrapper<StorageFile> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StorageFile::getStatus, 1); // 只查询正常状态的文件
        
        if (StringUtils.hasText(queryDTO.getKeyword())) {
            queryWrapper.like(StorageFile::getOriginalName, queryDTO.getKeyword());
        }
        
        if (StringUtils.hasText(queryDTO.getExtension())) {
            queryWrapper.eq(StorageFile::getExtension, queryDTO.getExtension());
        }
        
        if (queryDTO.getMinSize() != null) {
            queryWrapper.ge(StorageFile::getFileSize, queryDTO.getMinSize());
        }
        
        if (queryDTO.getMaxSize() != null) {
            queryWrapper.le(StorageFile::getFileSize, queryDTO.getMaxSize());
        }
        
        if (queryDTO.getStorageClass() != null) {
            queryWrapper.eq(StorageFile::getStorageClass, queryDTO.getStorageClass());
        }
        
        // 分页查询
        IPage<StorageFile> page = new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize());
        IPage<StorageFile> result = storageFileMapper.selectPage(page, queryWrapper);
        
        // 转换为VO列表
        List<FileVO> fileVOList = result.getRecords().stream()
                .map(this::convertToVO)
                .collect(Collectors.toList());

        return PageResult.ofPage(queryDTO.getPageNum(), queryDTO.getPageSize(), result.getTotal(), fileVOList);
    }
    

    
    /**
     * 将StorageFile转换为FileVO
     * @param storageFile StorageFile对象
     * @return FileVO对象
     */
    private FileVO convertToVO(StorageFile storageFile) {
        FileVO fileVO = new FileVO();
        fileVO.setId(storageFile.getId());
        fileVO.setFileKey(storageFile.getFileKey());
        fileVO.setOriginalName(storageFile.getOriginalName());
        fileVO.setFileName(storageFile.getOriginalName());
        fileVO.setFileSize(storageFile.getFileSize());
        fileVO.setSizeFormatted(FileUtils.formatFileSize(storageFile.getFileSize()));
        fileVO.setMimeType(storageFile.getMimeType());
        fileVO.setExtension(storageFile.getExtension());
        fileVO.setMd5(storageFile.getMd5());
        fileVO.setStatus(storageFile.getStatus());
        fileVO.setStorageClass(storageFile.getStorageClass());
        fileVO.setCreateTime(storageFile.getCreateTime());
        return fileVO;
    }
}